Package org.kc7bfi.jflac

Source Code of org.kc7bfi.jflac.FLACDecoder

package org.kc7bfi.jflac;

/**
*  libFLAC - Free Lossless Audio Codec library
* Copyright (C) 2000,2001,2002,2003  Josh Coalson
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA  02111-1307, USA.
*/

import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Vector;

import org.kc7bfi.jflac.frame.BadHeaderException;
import org.kc7bfi.jflac.frame.ChannelConstant;
import org.kc7bfi.jflac.frame.ChannelFixed;
import org.kc7bfi.jflac.frame.ChannelLPC;
import org.kc7bfi.jflac.frame.ChannelVerbatim;
import org.kc7bfi.jflac.frame.Frame;
import org.kc7bfi.jflac.frame.Header;
import org.kc7bfi.jflac.io.BitInputStream;
import org.kc7bfi.jflac.io.RandomFileInputStream;
import org.kc7bfi.jflac.metadata.Application;
import org.kc7bfi.jflac.metadata.CueSheet;
import org.kc7bfi.jflac.metadata.Metadata;
import org.kc7bfi.jflac.metadata.Padding;
import org.kc7bfi.jflac.metadata.Picture;
import org.kc7bfi.jflac.metadata.SeekPoint;
import org.kc7bfi.jflac.metadata.SeekTable;
import org.kc7bfi.jflac.metadata.StreamInfo;
import org.kc7bfi.jflac.metadata.Unknown;
import org.kc7bfi.jflac.metadata.VorbisComment;
import org.kc7bfi.jflac.util.ByteData;
import org.kc7bfi.jflac.util.CRC16;

/**
* A Java FLAC decoder.
* @author kc7bfi
*/
public class FLACDecoder {
    private static final int FRAME_FOOTER_CRC_LEN = 16; // bits
    private static final byte[] ID3V2_TAG = new byte[] { 'I', 'D', '3' };
   
    private BitInputStream bitStream;
    private ChannelData[] channelData = new ChannelData[Constants.MAX_CHANNELS];
    private int outputCapacity = 0;
    private int outputChannels = 0;
    private long samplesDecoded = 0;
    private StreamInfo streamInfo;
    private Frame frame = new Frame();
    private byte[] headerWarmup = new byte[2]; // contains the sync code and reserved bits
    //private int state;
    private int channels;
    private InputStream inputStream = null;
   
    private int badFrames;
    private boolean eof = false;
   
    private FrameListeners frameListeners = new FrameListeners();
    private PCMProcessors pcmProcessors = new PCMProcessors();
   
    // Decoder states
    //private static final int DECODER_SEARCH_FOR_METADATA = 0;
    //private static final int DECODER_READ_METADATA = 1;
    //private static final int DECODER_SEARCH_FOR_FRAME_SYNC = 2;
    //private static final int DECODER_READ_FRAME = 3;
    //private static final int DECODER_END_OF_STREAM = 4;
    //private static final int DECODER_ABORTED = 5;
    //private static final int DECODER_UNPARSEABLE_STREAM = 6;
    //private static final int STREAM_DECODER_MEMORY_ALLOCATION_ERROR = 7;
    //private static final int STREAM_DECODER_ALREADY_INITIALIZED = 8;
    //private static final int STREAM_DECODER_INVALID_CALLBACK = 9;
    //private static final int STREAM_DECODER_UNINITIALIZED = 10;
   
    /**
     * The constructor.
     * @param inputStream    The input stream to read data from
     */
    public FLACDecoder(InputStream inputStream) {
        this.inputStream = inputStream;
        this.bitStream = new BitInputStream(inputStream);
        samplesDecoded = 0;
        //state = DECODER_SEARCH_FOR_METADATA;
    }
   
    /**
     * Return the parsed StreamInfo Metadata record.
     * @return  The StreamInfo
     */
    public StreamInfo getStreamInfo() {
        return streamInfo;
    }
   
    /**
     * Return the ChannelData object.
     * @return  The ChannelData object
     */
    public ChannelData[] getChannelData() {
        return channelData;
    }
   
    /**
     * Return the input but stream.
     * @return  The bit stream
     */
    public BitInputStream getBitInputStream() {
        return bitStream;
    }
   
    /**
     * Add a frame listener.
     * @param listener  The frame listener to add
     */
    public void addFrameListener(FrameListener listener) {
        frameListeners.addFrameListener(listener);
    }
   
    /**
     * Remove a frame listener.
     * @param listener  The frame listener to remove
     */
    public void removeFrameListener(FrameListener listener) {
        frameListeners.removeFrameListener(listener);
    }
   
    /**
     * Add a PCM processor.
     * @param processor  The processor listener to add
     */
    public void addPCMProcessor(PCMProcessor processor) {
        pcmProcessors.addPCMProcessor(processor);
    }
   
    /**
     * Remove a PCM processor.
     * @param processor  The processor listener to remove
     */
    public void removePCMProcessor(PCMProcessor processor) {
        pcmProcessors.removePCMProcessor(processor);
    }
   
    private void callPCMProcessors(Frame frame) {
      ByteData bd = decodeFrame(frame, null);
        pcmProcessors.processPCM(bd);
    }
   
    /**
     * Fill the given ByteData object with PCM data from the frame.
     *
     * @param frame the frame to send to the PCM processors
     * @param pcmData the byte data to be filled, or null if it should be allocated
     * @return the ByteData that was filled (may be a new instance from <code>space</code>)
     */
    public ByteData decodeFrame(Frame frame, ByteData pcmData) {
      // required size of the byte buffer
      int byteSize = frame.header.blockSize * channels * ((streamInfo.getBitsPerSample() + 7) / 2);
      if (pcmData == null || pcmData.getData().length < byteSize ) {
        pcmData = new ByteData(byteSize);
      } else {
        pcmData.setLen(0);
      }
        if (streamInfo.getBitsPerSample() == 8) {
            for (int i = 0; i < frame.header.blockSize; i++) {
                for (int channel = 0; channel < channels; channel++) {
                    pcmData.append((byte) (channelData[channel].getOutput()[i] + 0x80));
                }
            }
        } else if (streamInfo.getBitsPerSample() == 16) {
            for (int i = 0; i < frame.header.blockSize; i++) {
                for (int channel = 0; channel < channels; channel++) {
                    short val = (short) (channelData[channel].getOutput()[i]);
                    pcmData.append((byte) (val & 0xff));
                    pcmData.append((byte) ((val >> 8) & 0xff));
                }
            }
        } else if (streamInfo.getBitsPerSample() == 24) {
            for (int i = 0; i < frame.header.blockSize; i++) {
                for (int channel = 0; channel < channels; channel++) {
                    int val = (channelData[channel].getOutput()[i]);
                    pcmData.append((byte) (val & 0xff));
                    pcmData.append((byte) ((val >> 8) & 0xff));
                    pcmData.append((byte) ((val >> 16) & 0xff));
                }
            }
        }
        return pcmData;
    }
   
    /**
     * Read the FLAC stream info.
     * @return  The FLAC Stream Info record
     * @throws IOException On read error
     */
    public StreamInfo readStreamInfo() throws IOException {
        readStreamSync();
        Metadata metadata = readNextMetadata();
        if (!(metadata instanceof StreamInfo)) throw new IOException("StreamInfo metadata block missing");
        return (StreamInfo) metadata;
    }
   
    /**
     * Read an array of metadata blocks.
     * @return  The array of metadata blocks
     * @throws IOException  On read error
     */
    public Metadata[] readMetadata() throws IOException {
        readStreamSync();
        Vector<Metadata> metadataList = new Vector<Metadata>();
        Metadata metadata;
        do {
            metadata = readNextMetadata();
            metadataList.add(metadata);
        } while (!metadata.isLast());
        return (Metadata[])metadataList.toArray(new Metadata[0]);
    }
   
    /**
     * Read an array of metadata blocks.
     * @param streamInfo    The StreamInfo metadata block previously read
     * @return  The array of metadata blocks
     * @throws IOException  On read error
     */
    public Metadata[] readMetadata(StreamInfo streamInfo) throws IOException {
        if (streamInfo.isLast()) return new Metadata[0];
        Vector<Metadata> metadataList = new Vector<Metadata>();
        Metadata metadata;
        do {
            metadata = readNextMetadata();
            metadataList.add(metadata);
        } while (!metadata.isLast());
        return (Metadata[])metadataList.toArray(new Metadata[0]);
    }
   
    /**
     * process a single metadata/frame.
     * @return True of one processed
     * @throws IOException  on read error
     */
    /*
     public boolean processSingle() throws IOException {
    
     while (true) {
     switch (state) {
     case DECODER_SEARCH_FOR_METADATA :
     readStreamSync();
     break;
     case DECODER_READ_METADATA :
     readNextMetadata(); // above function sets the status for us
     return true;
     case DECODER_SEARCH_FOR_FRAME_SYNC :
     frameSync(); // above function sets the status for us
     break;
     case DECODER_READ_FRAME :
     readFrame();
     return true; // above function sets the status for us
     //break;
      case DECODER_END_OF_STREAM :
      case DECODER_ABORTED :
      return true;
      default :
      return false;
      }
      }
      }
      */
   
    /**
     * Process all the metadata records.
     * @throws IOException On read error
     */
    /*
     public void processMetadata() throws IOException {
    
     while (true) {
     switch (state) {
     case DECODER_SEARCH_FOR_METADATA :
     readStreamSync();
     break;
     case DECODER_READ_METADATA :
     readNextMetadata(); // above function sets the status for us
     break;
     case DECODER_SEARCH_FOR_FRAME_SYNC :
     case DECODER_READ_FRAME :
     case DECODER_END_OF_STREAM :
     case DECODER_ABORTED :
     default :
     return;
     }
     }
     }
     */
   
    /**
     * Decode the FLAC file.
     * @throws IOException  On read error
     */
    public void decode() throws IOException {
        readMetadata();
        try {
            while (true) {
                //switch (state) {
                //case DECODER_SEARCH_FOR_METADATA :
                //    readStreamSync();
                //    break;
                //case DECODER_READ_METADATA :
                //    Metadata metadata = readNextMetadata();
                //    if (metadata == null) break;
                //    break;
                //case DECODER_SEARCH_FOR_FRAME_SYNC :
                findFrameSync();
                //    break;
                //case DECODER_READ_FRAME :
                try {
                    readFrame();
                    frameListeners.processFrame(frame);
                    callPCMProcessors(frame);
                } catch (FrameDecodeException e) {
                    badFrames++;
                }
                //    break;
                //case DECODER_END_OF_STREAM :
                //case DECODER_ABORTED :
                //    return;
                //default :
                //    throw new IOException("Unknown state: " + state);
                //}
            }
        } catch (EOFException e) {
            eof = true;
        }
    }
   
    /**
     * Decode the data frames.
     * @throws IOException  On read error
     */
    public void decodeFrames() throws IOException {
        //state = DECODER_SEARCH_FOR_FRAME_SYNC;
        try {
            while (true) {
                //switch (state) {
                //case DECODER_SEARCH_FOR_METADATA :
                //    readStreamSync();
                //    break;
                //case DECODER_READ_METADATA :
                //    Metadata metadata = readNextMetadata();
                //    if (metadata == null) break;
                //    break;
                //case DECODER_SEARCH_FOR_FRAME_SYNC :
                findFrameSync();
                //    break;
                //case DECODER_READ_FRAME :
                try {
                    readFrame();
                    frameListeners.processFrame(frame);
                    callPCMProcessors(frame);
                } catch (FrameDecodeException e) {
                    badFrames++;
                }
                //    break;
                //case DECODER_END_OF_STREAM :
                //case DECODER_ABORTED :
                //    return;
                //default :
                //    throw new IOException("Unknown state: " + state);
                //}
            }
        } catch (EOFException e) {
            eof = true;
        }
    }
   
    /**
     * Decode the data frames between two seek points.
     * @param from  The starting seek point
     * @param to    The ending seek point (non-inclusive)
     * @throws IOException  On read error
     */
    public void decode(SeekPoint from, SeekPoint to) throws IOException {
        // position random access file
        if (!(inputStream instanceof RandomFileInputStream)) throw new IOException("Not a RandomFileInputStream: " + inputStream.getClass().getName());
        ((RandomFileInputStream)inputStream).seek(from.getStreamOffset());
        bitStream.reset();
        samplesDecoded = from.getSampleNumber();
       
        //state = DECODER_SEARCH_FOR_FRAME_SYNC;
        try {
            while (true) {
                //switch (state) {
                //case DECODER_SEARCH_FOR_METADATA :
                //    readStreamSync();
                //    break;
                //case DECODER_READ_METADATA :
                //    Metadata metadata = readNextMetadata();
                //    if (metadata == null) break;
                //    break;
                //case DECODER_SEARCH_FOR_FRAME_SYNC :
                findFrameSync();
                //    break;
                //case DECODER_READ_FRAME :
                try {
                    readFrame();
                    frameListeners.processFrame(frame);
                    callPCMProcessors(frame);
                } catch (FrameDecodeException e) {
                    badFrames++;
                }
                //frameListeners.processFrame(frame);
                //callPCMProcessors(frame);
                //System.out.println(samplesDecoded +" "+ to.getSampleNumber());
                if (to != null && samplesDecoded >= to.getSampleNumber()) return;
                //    break;
                //case DECODER_END_OF_STREAM :
                //case DECODER_ABORTED :
                //    return;
                //default :
                //    throw new IOException("Unknown state: " + state);
                //}
            }
        } catch (EOFException e) {
            eof = true;
        }
    }
   
    /*
     private boolean processUntilEndOfStream() throws IOException {
     //boolean got_a_frame;
     
      while (true) {
      switch (state) {
      case DECODER_SEARCH_FOR_METADATA :
      readStreamSync();
      break;
      case DECODER_READ_METADATA :
      readNextMetadata(); // above function sets the status for us
      break;
      case DECODER_SEARCH_FOR_FRAME_SYNC :
      frameSync(); // above function sets the status for us
      //System.exit(0);
       break;
       case DECODER_READ_FRAME :
       readFrame();
       break;
       case DECODER_END_OF_STREAM :
       case DECODER_ABORTED :
       return true;
       default :
       return false;
       }
       }
       }
       */
   
    /**
     * Read the next data frame.
     * @return  The next frame
     * @throws IOException  on read error
     */
    public Frame readNextFrame() throws IOException {
        while (true) {
          try{
            findFrameSync();
          } catch (EOFException e) {
                eof = true;
                break;
            } catch (Exception e){
              bitStream.reset();
              continue;
            }
            try {
                readFrame();
                frameListeners.processFrame(frame);
                return frame;
            } catch (EOFException e) {
                break;
            } catch (Exception e) {
              badFrames++;
      }
        }
        return null;
    }
   
    /**
     * Bytes consumed.
     * @return  The number of bytes read
     */
    //public long getBytesConsumed() {
    //    return is.getConsumedBlurbs();
    //}
   
    /**
     * Bytes read.
     * @return  The number of bytes read
     */
    public long getTotalBytesRead() {
        return bitStream.getTotalBytesRead();
    }
   
    /*
     public int getInputBytesUnconsumed() {
     return is.getInputBytesUnconsumed();
     }
     */
   
    private void allocateOutput(int size, int channels) {
        if (size <= outputCapacity && channels <= outputChannels) return;
       
        for (int i = 0; i < Constants.MAX_CHANNELS; i++) {
            channelData[i] = null;
        }
       
        for (int i = 0; i < channels; i++) {
            channelData[i] = new ChannelData(size);
        }
       
        outputCapacity = size;
        outputChannels = channels;
    }
   
    /**
     * Read the stream sync string.
     * @throws IOException  On read error
     */
    private void readStreamSync() throws IOException {
        int id = 0;
        for (int i = 0; i < 4;) {
            int x = bitStream.readRawUInt(8);
            if (x == Constants.STREAM_SYNC_STRING[i]) {
                i++;
                id = 0;
            } else if (x == ID3V2_TAG[id]) {
                id++;
                i = 0;
                if (id == 3) {
                    skipID3v2Tag();
                    id = 0;
                }
            } else {
                throw new IOException("Could not find Stream Sync");
                //i = 0;
                //id = 0;
            }
        }
    }
   
    /**
     * Read a single metadata record.
     * @return  The next metadata record
     * @throws IOException  on read error
     */
    public Metadata readNextMetadata() throws IOException {
        Metadata metadata = null;
       
        boolean isLast = (bitStream.readRawUInt(Metadata.STREAM_METADATA_IS_LAST_LEN) != 0);
        int type = bitStream.readRawUInt(Metadata.STREAM_METADATA_TYPE_LEN);
        int length = bitStream.readRawUInt(Metadata.STREAM_METADATA_LENGTH_LEN);
       
        if (type == Metadata.METADATA_TYPE_STREAMINFO) {
            streamInfo = new StreamInfo(bitStream, length, isLast);
            metadata = streamInfo;
            pcmProcessors.processStreamInfo((StreamInfo)metadata);
        } else if (type == Metadata.METADATA_TYPE_SEEKTABLE) {
            metadata = new SeekTable(bitStream, length, isLast);
        } else if (type == Metadata.METADATA_TYPE_APPLICATION) {
            metadata = new Application(bitStream, length, isLast);
        } else if (type == Metadata.METADATA_TYPE_PADDING) {
            metadata = new Padding(bitStream, length, isLast);
        } else if (type == Metadata.METADATA_TYPE_VORBIS_COMMENT) {
            metadata = new VorbisComment(bitStream, length, isLast);
        } else if (type == Metadata.METADATA_TYPE_CUESHEET) {
      metadata = new CueSheet(bitStream, length, isLast);
  } else if (type == Metadata.METADATA_TYPE_PICTURE) {
      metadata = new Picture(bitStream, length, isLast);
        } else {
            metadata = new Unknown(bitStream, length, isLast);
        }
        frameListeners.processMetadata(metadata);
        //if (isLast) state = DECODER_SEARCH_FOR_FRAME_SYNC;
        return metadata;
    }
   
   
    private void skipID3v2Tag() throws IOException {
       
        bitStream.readRawInt(8);
        bitStream.readRawInt(8);
        bitStream.readRawInt(8);
       
        // get the size (in bytes) to skip
        int skip = 0;
        for (int i = 0; i < 4; i++) {
            int x = bitStream.readRawUInt(8);
            skip <<= 7;
            skip |= (x & 0x7f);
        }
       
        // skip the rest of the tag
        bitStream.readByteBlockAlignedNoCRC(null, skip);
    }
   
    private void findFrameSync() throws IOException {
        boolean first = true;
        //int cnt=0;
       
        // If we know the total number of samples in the stream, stop if we've read that many.
        // This will stop us, for example, from wasting time trying to sync on an ID3V1 tag.
//        if (streamInfo != null && (streamInfo.getTotalSamples() != 0)) {
//            if (samplesDecoded >= streamInfo.getTotalSamples()) {
//                //state = DECODER_END_OF_STREAM;
//                return;
//            }
//        }
       
        // make sure we're byte aligned
        if (!bitStream.isConsumedByteAligned()) {
            bitStream.readRawUInt(bitStream.bitsLeftForByteAlignment());
        }
       
        int x;
        try {
            while (true) {
                x = bitStream.readRawUInt(8);
                if (x == 0xff) { // MAGIC NUMBER for the first 8 frame sync bits
                    headerWarmup[0] = (byte) x;
                    x = bitStream.peekRawUInt(8);
                   
                    /* we have to check if we just read two 0xff's in a row; the second may actually be the beginning of the sync code */
                    /* else we have to check if the second byte is the end of a sync code */
                    if (x >> 2 == 0x3e) { /* MAGIC NUMBER for the last 6 sync bits */
                        headerWarmup[1] = (byte) bitStream.readRawUInt(8);
                        //state = DECODER_READ_FRAME;
                        return;
                    }
                }
                if (first) {
                    frameListeners.processError("FindSync LOST_SYNC: " + Integer.toHexString((x & 0xff)));
                    first = false;
                }
            }
        } catch (IOException e) {
            if (!first) frameListeners.processError("FindSync LOST_SYNC: Left over data in file");
        }
    }
   
    /**
     * Read the next data frame.
     * @throws IOException  On read error
     * @throws FrameDecodeException On frame decoding error
     */
    public void readFrame() throws IOException, FrameDecodeException {
        int channel;
        int i;
        int mid, side, left, right;
        short frameCRC; /* the one we calculate from the input stream */
        //int x;
       
        /* init the CRC */
        frameCRC = 0;
        frameCRC = CRC16.update(headerWarmup[0], frameCRC);
        frameCRC = CRC16.update(headerWarmup[1], frameCRC);
        bitStream.resetReadCRC16(frameCRC);
       
        try {
            frame.header = new Header(bitStream, headerWarmup, streamInfo);
        } catch (BadHeaderException e) {
            frameListeners.processError("Found bad header: " + e);
            throw new FrameDecodeException("Bad Frame Header: " + e);
        }
        //if (state == DECODER_SEARCH_FOR_FRAME_SYNC) return false;
        allocateOutput(frame.header.blockSize, frame.header.channels);
        for (channel = 0; channel < frame.header.channels; channel++) {
            // first figure the correct bits-per-sample of the subframe
            int bps = frame.header.bitsPerSample;
            switch (frame.header.channelAssignment) {
            case Constants.CHANNEL_ASSIGNMENT_INDEPENDENT :
                /* no adjustment needed */
                break;
            case Constants.CHANNEL_ASSIGNMENT_LEFT_SIDE :
                if (channel == 1)
                    bps++;
            break;
            case Constants.CHANNEL_ASSIGNMENT_RIGHT_SIDE :
                if (channel == 0)
                    bps++;
            break;
            case Constants.CHANNEL_ASSIGNMENT_MID_SIDE :
                if (channel == 1)
                    bps++;
            break;
            default :
            }
            // now read it
            try {
                readSubframe(channel, bps);
            } catch (IOException e) {
                frameListeners.processError("ReadSubframe: " + e);
                throw e;
            }
        }
        readZeroPadding();
       
        // Read the frame CRC-16 from the footer and check
        frameCRC = bitStream.getReadCRC16();
        frame.setCRC((short)bitStream.readRawUInt(FRAME_FOOTER_CRC_LEN));
        if (frameCRC == frame.getCRC()) {
            /* Undo any special channel coding */
            switch (frame.header.channelAssignment) {
            case Constants.CHANNEL_ASSIGNMENT_INDEPENDENT :
                /* do nothing */
                break;
            case Constants.CHANNEL_ASSIGNMENT_LEFT_SIDE :
                for (i = 0; i < frame.header.blockSize; i++)
                    channelData[1].getOutput()[i] = channelData[0].getOutput()[i] - channelData[1].getOutput()[i];
            break;
            case Constants.CHANNEL_ASSIGNMENT_RIGHT_SIDE :
                for (i = 0; i < frame.header.blockSize; i++)
                    channelData[0].getOutput()[i] += channelData[1].getOutput()[i];
            break;
            case Constants.CHANNEL_ASSIGNMENT_MID_SIDE :
                for (i = 0; i < frame.header.blockSize; i++) {
                    mid = channelData[0].getOutput()[i];
                    side = channelData[1].getOutput()[i];
                    mid <<= 1;
                    if ((side & 1) != 0) // i.e. if 'side' is odd...
                        mid++;
                    left = mid + side;
                    right = mid - side;
                    channelData[0].getOutput()[i] = left >> 1;
                    channelData[1].getOutput()[i] = right >> 1;
                }
            //System.exit(1);
            break;
            default :
                break;
            }
        } else {
            // Bad frame, emit error and zero the output signal
            frameListeners.processError("CRC Error: " + Integer.toHexString((frameCRC & 0xffff)) + " vs " + Integer.toHexString((frame.getCRC() & 0xffff)));
            for (channel = 0; channel < frame.header.channels; channel++) {
                for (int j = 0; j < frame.header.blockSize; j++)
                    channelData[channel].getOutput()[j] = 0;
            }
        }
       
        // put the latest values into the public section of the decoder instance
        channels = frame.header.channels;
        //samplesDecoded = frame.header.sampleNumber + frame.header.blockSize;
        samplesDecoded += frame.header.blockSize;
        //System.out.println(samplesDecoded+" "+frame.header.sampleNumber + " "+frame.header.blockSize);
       
        //state = DECODER_SEARCH_FOR_FRAME_SYNC;
        //return;
    }
   
    private void readSubframe(int channel, int bps) throws IOException, FrameDecodeException {
        int x;
       
        x = bitStream.readRawUInt(8); /* MAGIC NUMBER */
       
        boolean haveWastedBits = ((x & 1) != 0);
        x &= 0xfe;
       
        int wastedBits = 0;
        if (haveWastedBits) {
            wastedBits = bitStream.readUnaryUnsigned() + 1;
            bps -= wastedBits;
        }
       
        // Lots of magic numbers here
        if ((x & 0x80) != 0) {
            frameListeners.processError("ReadSubframe LOST_SYNC: " + Integer.toHexString(x & 0xff));
            //state = DECODER_SEARCH_FOR_FRAME_SYNC;
            throw new FrameDecodeException("ReadSubframe LOST_SYNC: " + Integer.toHexString(x & 0xff));
            //return true;
        } else if (x == 0) {
            frame.subframes[channel] = new ChannelConstant(bitStream, frame.header, channelData[channel], bps, wastedBits);
        } else if (x == 2) {
            frame.subframes[channel] = new ChannelVerbatim(bitStream, frame.header, channelData[channel], bps, wastedBits);
        } else if (x < 16) {
            //state = DECODER_UNPARSEABLE_STREAM;
            throw new FrameDecodeException("ReadSubframe Bad Subframe Type: " + Integer.toHexString(x & 0xff));
        } else if (x <= 24) {
            //FLACSubframe_Fixed subframe = read_subframe_fixed_(channel, bps, (x >> 1) & 7);
            frame.subframes[channel] = new ChannelFixed(bitStream, frame.header, channelData[channel], bps, wastedBits, (x >> 1) & 7);
        } else if (x < 64) {
            //state = DECODER_UNPARSEABLE_STREAM;
            throw new FrameDecodeException("ReadSubframe Bad Subframe Type: " + Integer.toHexString(x & 0xff));
        } else {
            frame.subframes[channel] = new ChannelLPC(bitStream, frame.header, channelData[channel], bps, wastedBits, ((x >> 1) & 31) + 1);
        }
        if (haveWastedBits) {
            int i;
            x = frame.subframes[channel].getWastedBits();
            for (i = 0; i < frame.header.blockSize; i++)
                channelData[channel].getOutput()[i] <<= x;
        }
    }
   
    private void readZeroPadding() throws IOException, FrameDecodeException {
        if (!bitStream.isConsumedByteAligned()) {
            int zero = bitStream.readRawUInt(bitStream.bitsLeftForByteAlignment());
            if (zero != 0) {
                frameListeners.processError("ZeroPaddingError: " + Integer.toHexString(zero));
                //state = DECODER_SEARCH_FOR_FRAME_SYNC;
                throw new FrameDecodeException("ZeroPaddingError: " + Integer.toHexString(zero));
            }
        }
    }
   
    /**
     * Get the number of samples decoded.
     * @return Returns the samples Decoded.
     */
    public long getSamplesDecoded() {
        return samplesDecoded;
    }
    /**
     * @return Returns the number of bad frames decoded.
     */
    public int getBadFrames() {
        return badFrames;
    }
    /**
     * @return Returns true if end-of-file.
     */
    public boolean isEOF() {
        return eof;
    }
   
    public void setSamplesDecoded(long samples){
      samplesDecoded = samples;
    }
}
TOP

Related Classes of org.kc7bfi.jflac.FLACDecoder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.